package parse

import (
	"showen/ast"
	"showen/lex"
	"testing"
)

func TestParseExpression(t *testing.T) {
	tests := []struct {
		input      string
		wantType   ast.ExpType
		wantString string
	}{
		{"#t", ast.BOOLEAN_EXP, "#t"},
		{"1", ast.NUMBER_EXP, "1"},
		{"1/2", ast.NUMBER_EXP, "1/2"},
		{"2/4", ast.NUMBER_EXP, "1/2"},
		{"1.23", ast.NUMBER_EXP, "1.23"},
		{"1/2+0.34i", ast.NUMBER_EXP, "1/2+0.34i"},
		{"abc", ast.IDENTIFIER_EXP, "abc"},
		{`"abc"`, ast.STRING_EXP, `"abc"`},
		{`(define x 1)`, ast.LIST_EXP, "(define x 1)"},
		{`#(0 (2 2 2 2) "Anna")`, ast.VECTOR_EXP, `#(0 (2 2 2 2) "Anna")`},
		{"'sym", ast.QUOTE_EXP, "'sym"},
		{"(quote t)", ast.QUOTE_EXP, "'t"},
		{"(quote (a b c))", ast.QUOTE_EXP, "'(a b c)"},
		{"(quote (123 #f 2/4))", ast.QUOTE_EXP, "'(123 #f 1/2)"},
		{"'(1234.5 #f 2/4)", ast.QUOTE_EXP, "'(1234.5 #f 1/2)"},
		{`(define x 1000);this is (a test)`, ast.LIST_EXP, "(define x 1000)"},
		{";before\n(define x 5000);this is (a test)", ast.LIST_EXP, "(define x 5000)"},
		{"(quote ())", ast.QUOTE_EXP, "'()"},
		{"(quote (a . b))", ast.QUOTE_EXP, "'(a . b)"},
		{"'(a . b)", ast.QUOTE_EXP, "'(a . b)"},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		parser := NewParser(tokens)
		expr, err := parser.ParseExpression()
		if err != nil {
			t.Fatalf("parser.ParseExpression() error = %v", err)
		}

		if expr.Type() != tt.wantType {
			t.Errorf("parser.ParseExpression() type = %v, want %v", expr.Type(), tt.wantType)
		}

		if expr.String() != tt.wantString {
			t.Errorf("parser.ParseExpression() string = %v, want %v", expr.String(), tt.wantString)
		}
	}
}

func TestParseProgram(t *testing.T) {
	tests := []struct {
		input string
		want  []string
	}{
		{
			input: "1 2",
			want:  []string{"1", "2"},
		},
		{
			input: "(define x 1) (define y 2)",
			want:  []string{"(define x 1)", "(define y 2)"},
		},
		{
			input: "'a 'b",
			want:  []string{"'a", "'b"},
		},
		{
			input: "(quote a) 123",
			want:  []string{"'a", "123"},
		},
		{
			input: "123 (quote b)",
			want:  []string{"123", "'b"},
		},
		{
			input: "(quote b) (quote b)",
			want:  []string{"'b", "'b"},
		},
	}

	for _, tt := range tests {
		lexer := lex.NewLexer(tt.input)
		tokens := lexer.ReadTokens()
		//fmt.Println(tokens)
		parser := NewParser(tokens)
		exprs, err := parser.ParseProgram()
		if err != nil {
			t.Fatalf("parser.ParseProgram() error = %v", err)
		}
		for i, expr := range exprs {
			if expr.String() != tt.want[i] {
				t.Errorf("parser.ParseProgram() string = %v, want %v", expr.String(), tt.want[i])
			}
		}
	}
}
